////////////////////////////////////////////////////////////////////////////////
//
//  ADOBE SYSTEMS INCORPORATED
//  Copyright 2011 Adobe Systems Incorporated
//  All Rights Reserved.
//
//  NOTICE: Adobe permits you to use, modify, and distribute this file
//  in accordance with the terms of the license agreement accompanying it.
//
////////////////////////////////////////////////////////////////////////////////

package 
{
    import flash.events.EventDispatcher;
    import mx.collections.CursorBookmark;
    import mx.collections.ICollectionView;
    import mx.collections.IHierarchicalCollectionViewCursor;
    import mx.collections.IList;
    import mx.collections.IViewCursor;
    import mx.events.CollectionEvent;

    /** 
     *  A class that takes an ICollectionView that isn't also an IList
     *  and makes it work like an IList.
     */
    public class ICVIListAdapter extends EventDispatcher implements IList
    {

        private var icv:ICollectionView;
        private var cursor:IViewCursor;
        private var currentIndex:int;
        
        public function ICVIListAdapter(icv:ICollectionView)
        {
            super();
            this.icv = icv;
            icv.addEventListener(CollectionEvent.COLLECTION_CHANGE, collectionChangeHandler, false, -100);
            
            cursor = icv.createCursor();
            currentIndex = length - 1;
        }

        //--------------------------------------------------------------------------
        //
        //  Properties
        //
        //--------------------------------------------------------------------------
        
        //----------------------------------
        //  length
        //----------------------------------
        
        /**
         *  The number of items in this collection. 
         *  0 means no items while -1 means the length is unknown. 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function get length():int
        {
            return icv.length;
        }
        
        //----------------------------------
        //  originalView
        //----------------------------------
        
        /**
         *  The number of items in this collection. 
         *  0 means no items while -1 means the length is unknown. 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function get originalView():ICollectionView
        {
            return icv;
        }
        
        //--------------------------------------------------------------------------
        //
        //  Methods
        //
        //--------------------------------------------------------------------------
        
        /**
         *  Adds the specified item to the end of the list.
         *  Equivalent to <code>addItemAt(item, length)</code>.
         *
         *  @param item The item to add.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function addItem(item:Object):void
        {
            cursor.seek(CursorBookmark.LAST);
            cursor.insert(item);
            currentIndex = length;  // TODO (aharui) verify this
        }
        
        /**
         *  Adds the item at the specified index.  
         *  The index of any item greater than the index of the added item is increased by one.  
         *  If the the specified index is less than zero or greater than the length
         *  of the list, a RangeError is thrown.
         * 
         *  @param item The item to place at the index.
         *
         *  @param index The index at which to place the item.
         *
         *  @throws RangeError if index is less than 0 or greater than the length of the list. 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function addItemAt(item:Object, index:int):void
        {
            cursor.seek(CursorBookmark.FIRST, index);
            cursor.insert(item);
            currentIndex = index;            
        }
        
        /**
         *  Gets the item at the specified index.
         * 
         *  @param index The index in the list from which to retrieve the item.
         *
         *  @param prefetch An <code>int</code> indicating both the direction
         *  and number of items to fetch during the request if the item is
         *  not local.
         *
         *  @return The item at that index, or <code>null</code> if there is none.
         *
         *  @throws mx.collections.errors.ItemPendingError if the data for that index needs to be 
         *  loaded from a remote location.
         *
         *  @throws RangeError if <code>index &lt; 0</code>
         *  or <code>index >= length</code>.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function getItemAt(index:int, prefetch:int = 0):Object
        {
            if (index == currentIndex)
                return cursor.current;
            
            if (currentIndex + 1 == index)
            {
                cursor.moveNext();
                currentIndex++;
                return cursor.current;
            }
            if (currentIndex - 1 == index)
            {
                cursor.movePrevious();
                currentIndex--;
                return cursor.current;
            }
            
            if (index < length - index)
                cursor.seek(CursorBookmark.FIRST, index);
            else
                cursor.seek(CursorBookmark.LAST, index - length);
            currentIndex = index;
            return cursor.current;
        }
        
        /**
         *  Returns the index of the item if it is in the list such that
         *  getItemAt(index) == item.
         * 
         *  <p>Note: unlike <code>IViewCursor.find<i>xxx</i>()</code> methods,
         *  The <code>getItemIndex()</code> method cannot take a parameter with 
         *  only a subset of the fields in the item being serched for; 
         *  this method always searches for an item that exactly matches
         *  the input parameter.</p>
         * 
         *  @param item The item to find.
         *
         *  @return The index of the item, or -1 if the item is not in the list.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function getItemIndex(item:Object):int
        {
            if (cursor.current === item)
                return currentIndex;
            
            if (cursor.findFirst(item))
			{
		        if (cursor is IHierarchicalCollectionViewCursor && "index" in cursor)
		        {
		            currentIndex = cursor["index"];
		        }
		        else
		        {
		            var bookmark:CursorBookmark = cursor.bookmark;
		            currentIndex = bookmark.getViewIndex();
		            if (currentIndex == -1)
		                trace("getViewIndex may not be supported by this IViewCursor")
		        }
				return currentIndex;
			}
			else
			{
				cursor.seek(CursorBookmark.FIRST);
				currentIndex = 0;
			}
			return -1;
        }
        
        /**
         *  Notifies the view that an item has been updated.  
         *  This is useful if the contents of the view do not implement 
         *  <code>IEventDispatcher</code> and dispatches a 
         *  <code>PropertyChangeEvent</code>.  
         *  If a property is specified the view may be able to optimize its 
         *  notification mechanism.
         *  Otherwise it may choose to simply refresh the whole view.
         *
         *  @param item The item within the view that was updated.
         *
         *  @param property The name of the property that was updated.
         *
         *  @param oldValue The old value of that property. (If property was null,
         *  this can be the old value of the item.)
         *
         *  @param newValue The new value of that property. (If property was null,
         *  there's no need to specify this as the item is assumed to be
         *  the new value.)
         *
         *  @see mx.events.CollectionEvent
         *  @see mx.events.PropertyChangeEvent
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function itemUpdated(item:Object, property:Object = null, 
                             oldValue:Object = null, 
                             newValue:Object = null):void
        {
            icv.itemUpdated(item, property, oldValue, newValue);
        }
        
        /** 
         *  Removes all items from the list.
         *
         *  <p>If any item is not local and an asynchronous operation must be
         *  performed, an <code>ItemPendingError</code> will be thrown.</p>
         *
         *  <p>See the ItemPendingError documentation as well as
         *  the collections documentation for more information
         *   on using the <code>ItemPendingError</code>.</p>
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function removeAll():void
        {
            currentIndex = 0;
            cursor.seek(CursorBookmark.FIRST);
            while (icv.length > 0)
            {
                cursor.remove();    
            }
        }
        
        /**
         *  Removes the item at the specified index and returns it.  
         *  Any items that were after this index are now one index earlier.
         *
         *  @param index The index from which to remove the item.
         *
         *  @return The item that was removed.
         *
         *  @throws RangeError is index is less than 0 or greater than length. 
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function removeItemAt(index:int):Object
        {
            var old:Object = getItemAt(index);
            cursor.remove();
            currentIndex = length > 0 ? index : -1;                        
            return old;
        }
        
        /**
         *  Places the item at the specified index.  
         *  If an item was already at that index the new item will replace it
         *  and it will be returned.
         *
         *  @param item The new item to be placed at the specified index.
         *
         *  @param index The index at which to place the item.
         *
         *  @return The item that was replaced, or <code>null</code> if none.
         *
         *  @throws RangeError if index is less than 0 or greater than length.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */
        public function setItemAt(item:Object, index:int):Object
        {
            var old:Object = getItemAt(index);
            cursor.remove();
            cursor.insert(item);
            return old;
        }
        
        /**
         *  Returns an Array that is populated in the same order as the IList
         *  implementation.
         *  This method can throw an ItemPendingError.
         *
         *  @return The array.
         *  
         *  @throws mx.collections.errors.ItemPendingError If the data is not yet completely loaded
         *  from a remote location.
         *  
         *  @langversion 3.0
         *  @playerversion Flash 9
         *  @playerversion AIR 1.1
         *  @productversion Flex 3
         */ 
        public function toArray():Array
        {
            var arr:Array = [];
            var temp:IViewCursor = icv.createCursor();
            while (!temp.afterLast)
            {
                arr.push(temp.current);
                temp.moveNext();
            }
            return arr;
        }
        
        
        // forward all change events
        private function collectionChangeHandler(event:CollectionEvent):void
        {
            if (cursor is IHierarchicalCollectionViewCursor && "index" in cursor)
            {
                currentIndex = cursor["index"];
            }
            else
            {
                var bookmark:CursorBookmark = cursor.bookmark;
                currentIndex = bookmark.getViewIndex();
                if (currentIndex == -1)
                    trace("getViewIndex may not be supported by this IViewCursor")
            }
            if (currentIndex == -1 && length > 0)
            {
                cursor.seek(CursorBookmark.FIRST);
                currentIndex = 0;
            }
            dispatchEvent(event);   
        }
    }
}